/********************************************************************************
 * @file    app_key.c
 * @author  jianqiang.xue
 * @version V1.0.0
 * @date    2021-04-03
 * @brief   按键扫描
 ********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stdbool.h>

#include "app_key.h"
#include "bsp_key.h"
#include "os_api.h"
#include "log.h"

/* Private Includes ----------------------------------------------------------*/
#include "ls_gpio.h"
#include "ls_syscfg.h"

/* Private Define ------------------------------------------------------------*/
/* Private Enum --------------------------------------------------------------*/
// 定义按键按下/松开状态
#define KEY_EVENT_NULL    0
#define KEY_EVENT_RELEASE 1

// 定义按键按下次数
typedef enum {
    KEY_NOT_PRESS = 0,
    KEY_ONE_TIMES,
    KEY_TWO_TIMES,
    KEY_LOOG_TIMES,
    KEY_LOOG_LOOG_TIMES,
} key_times_t;

/* Private Struct -----------------------------------------------------------*/
typedef struct {
    bool              io_ste;
    key_times_t       press_times; // 按下次数
    key_event_t       event;
    uint16_t          press_time;
    uint16_t          release_time;
} key_state_t;

/* External Variables --------------------------------------------------------*/
extern const key_fun_callback_t key_fun_callback[];

/* Private Variables ---------------------------------------------------------*/

// 定义按键 按下的电平状态 bit0--按键0 依次类推，最大支持32个按键
#ifndef __LS_SYSCFG_H
#else
static const uint32_t g_key_event_release_state = LS_KEY_EVENT_RELEASE_LEVEL;

// 定义按键 是否进行扫描(有些按键可以不需要扫描) bit0--按键0 依次类推，最大支持32个按键
static const uint32_t g_key_whether_scan_en = LS_KEY_WHETHER_SCAN_EN;

// 定义按键状态结构体，用于记录按键的按下/抬起时间
key_state_t g_key_state[LS_KEY_NUM] = {0};

/* 需要在业务层进行定义，下方是例子
void demo(void) {
    bsp_led_toggle(LED0);
}

const key_fun_callback_t key_fun_callback[] = {
    {KEY0, KEY_SINGLE_CLICK, demo}
};
*/

// 定义软定时器，用于扫描io
static void timer_key_scan(void const *arg);
static os_timer_id key_scan_id = NULL;
os_timer_def(k_scan, timer_key_scan);

/* Private Function Prototypes ----------------------------------------------*/
/**
 * @brief  按键扫描
 * @retval 0--无按键状态 1--有按键状态
 */
static uint8_t scan_key(void) {
    uint8_t sign = KEY_NULL;
#if LS_KEY_AUTOMATIC_SCANNING
    uint8_t times = 0;
#endif
    uint8_t not_scan_num = 0;

    for (uint8_t i = 0; i < LS_KEY_NUM; i++) {
        if (g_key_whether_scan_en[i] != true) {
            not_scan_num++;
            continue;
        }
        if (bsp_key_get_state((bsp_key_t)i) == (uint8_t)g_key_event_release_state[i]) {
            g_key_state[i].press_time++;
            if (g_key_state[i].release_time != 0)
                sign = 1;
            g_key_state[i].release_time = 0;
            g_key_state[i].event = KEY_EVENT_RELEASE;
            if (g_key_state[i].press_time >= 0xFFFD)
                g_key_state[i].press_time = 0xFFFD;

            if (g_key_state[i].io_ste == KEY_EVENT_NULL) {
                if (g_key_state[i].press_times == KEY_NOT_PRESS) {
                    g_key_state[i].press_times = KEY_ONE_PRESS;
                    g_key_state[i].event = KEY_EVENT_RELEASE;
                    sign = 1;
                    continue;
                } else if (g_key_state[i].press_times == KEY_ONE_PRESS &&
                           g_key_state[i].press_time >= (LS_KEY_SINGLE_CLICK_TIME / LS_KEY_SCAN_CYCLE_MS) &&
                           g_key_state[i].press_time < (LS_KEY_LONG_PRESS_TIME / LS_KEY_SCAN_CYCLE_MS)) {
                    g_key_state[i].press_times = KEY_TWO_PRESS;
                    g_key_state[i].event = KEY_TWO_PRESS;
                    g_key_state[i].press_time = 0;
                    sign = 1;
                    continue;
                }
            }
            g_key_state[i].event = KEY_EVENT_RELEASE;
            g_key_state[i].io_ste = KEY_EVENT_RELEASE;
            if (g_key_state[i].press_time == (LS_KEY_LONG_PRESS_TIME / LS_KEY_SCAN_CYCLE_MS) && g_key_state[i].press_times == KEY_ONE_PRESS) {
                g_key_state[i].event = KEY_LONG_PRESS;
                g_key_state[i].press_times = KEY_LOOG_PRESS;
                sign = 2;
                continue;
            } else if (g_key_state[i].press_time == (LS_KEY_LONG_LONG_PRESS_TIME / LS_KEY_SCAN_CYCLE_MS) && g_key_state[i].press_times == KEY_LOOG_PRESS) {
                g_key_state[i].event = KEY_LONG_LONG_PRESS;
                g_key_state[i].press_times = KEY_LOOG_LOOG_PRESS;
                sign = 3;
                continue;
            }
        } else {
            g_key_state[i].release_time++;
            if (g_key_state[i].press_times == KEY_ONE_PRESS &&
                g_key_state[i].release_time == (LS_KEY_EVENT_NULL_TIME / LS_KEY_SCAN_CYCLE_MS)) {
                g_key_state[i].press_time = 0;
                g_key_state[i].event = KEY_SINGLE_CLICK;
                sign = 1;
                continue;
            } else if (g_key_state[i].press_times == KEY_TWO_PRESS &&
                       g_key_state[i].release_time == (LS_KEY_DOUBLE_CLICK_TIME / LS_KEY_SCAN_CYCLE_MS)) {
                g_key_state[i].press_time = 0;
                g_key_state[i].press_times = KEY_ONE_PRESS;
                g_key_state[i].event = KEY_DOUBLE_CLICK;
                sign = 1;
                continue;
            } else if (g_key_state[i].release_time > (LS_KEY_EVENT_NULL_TIME / LS_KEY_SCAN_CYCLE_MS)) {
                g_key_state[i].press_time = 0;
                g_key_state[i].press_times = KEY_NOT_PRESS;
                g_key_state[i].event = KEY_NULL;
            }

            g_key_state[i].io_ste = KEY_EVENT_NULL;
            if (g_key_state[i].release_time == 1 && g_key_state[i].press_times > 0) {
                g_key_state[i].event = KEY_EVENT_NULL;
                sign = 1;
                continue;
            }

            if (g_key_state[i].release_time > 0xFD) {
                g_key_state[i].release_time = 0xFD;
#if LS_KEY_AUTOMATIC_SCANNING
                if (times == (LS_KEY_NUM - not_scan_num)) {
                    app_key_scan_stop();
                    g_key_state[i].release_time = 0;
                    g_key_state[i].press_time = 0;
                }
#endif
            }
        }
    }
    return sign;
}

/**
 * @brief  根据按键事件执行绑定的按键回调函数
 * @param  ch: 按键号
 * @param  event: 按键事件
 */
static void key_run_callback(uint8_t ch, uint8_t event) {
    for (uint8_t i = 0; i < 255; i++) {
        if (key_fun_callback[i].key_ch == 0xFF)
            return;
        if (key_fun_callback[i].key_ch == ch && key_fun_callback[i].event == event) {
            if ((key_fun_callback[i].callback) != NULL)
                (key_fun_callback[i].callback)();
        }
    }
}

/**
 * @brief  [软定时器回调函数] 轮训按键状态并回调对应函数
 */
static void timer_key_scan(void const* arg) {
    if (scan_key() != 0) {
        for (uint8_t i = 0; i < LS_KEY_NUM; i++) {
            switch (g_key_state[i].event) {
                case KEY_NULL:
                    break;
                case KEY_EVENT_NULL:
                    key_run_callback(i, KEY_EVENT_NULL);
                    break;
                case KEY_EVENT_RELEASE:
                    key_run_callback(i, KEY_EVENT_RELEASE);
                    break;
                case KEY_TWO_PRESS:
                    key_run_callback(i, KEY_TWO_PRESS);
                    key_run_callback(i, KEY_EVENT_RELEASE);
                    //                    LOG_D("<DEBUG> k_scan [%d] KEY_TWO_PRESS\r\n", i);
                    break;
                case KEY_SINGLE_CLICK:
                    key_run_callback(i, KEY_SINGLE_CLICK);
                    //                    LOG_D("<DEBUG> k_scan [%d] KEY_SINGLE_CLICK\r\n", i);
                    break;
                case KEY_DOUBLE_CLICK:
                    key_run_callback(i, KEY_DOUBLE_CLICK);
                    //                    LOG_D("<DEBUG> k_scan [%d] KEY_DOUBLE_CLICK\r\n", i);
                    break;
                case KEY_LONG_PRESS:
                    key_run_callback(i, KEY_LONG_PRESS);
                    //                    LOG_D("<DEBUG> k_scan [%d] KEY_LONG_PRESS\r\n", i);
                    break;
                case KEY_LONG_LONG_PRESS:
                    key_run_callback(i, KEY_LONG_LONG_PRESS);
                    //                    LOG_D("<DEBUG> k_scan [%d] KEY_LONG_LONG_PRESS\r\n", i);
                    break;
                default:
                    break;
            }
            g_key_state[i].event = KEY_NULL;
        }
    }
}

/* Public Function Prototypes -----------------------------------------------*/
/**
 * @brief  [按键扫描] 软定时器创建
 * @retval 0--失败 1--成功
 */
bool app_key_init(void) {
    uint8_t timer_ret = 0;
    bsp_key_init();
    key_scan_id = os_timer_create(os_timer(k_scan), OS_TIMER_PERIODIC, &timer_ret);
    if (!key_scan_id) {
        LOGE("k_scan timer create fail");
        return false;
    }
    return true;
}

/**
 * @brief  [按键扫描] 开启扫描
 * @retval 0--失败 1--成功
 */
bool app_key_scan_start(void) {
    if (os_timer_start(key_scan_id, LS_KEY_SCAN_CYCLE_MS) != OS_OK) {
        LOGE("k_scan timer start fail");
        return false;
    }
    return true;
}

/**
 * @brief  [按键扫描] 停止扫描
 * @retval 0--失败 1--成功
 */
bool app_key_scan_stop(void) {
    if (os_timer_stop(key_scan_id) != OS_OK) {
        LOGE("k_scan timer stop fail");
        return false;
    }
    return true;
}
#endif

